#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

#define SIZE 512
#define NUM 32
#define SEP " "
#define SkipPath(p) do{ p += (strlen(p)-1); while(*p != '/') p--; } while(0)

char* gArgv[NUM];
char cwd[SIZE*2];
int lastcode = 0;

const char* GetUserName()
{
 const char* username = getenv("USER");
 if (username==NULL) return "None";
 return username;
}

const char* GetHostName()
{
  const char* hostname = getenv("HOSTNAME");
  if (hostname == NULL) return "None";
  return hostname;
}

const char* GetCwd()
{
  const char* pwd = getenv("PWD");
  if (pwd == NULL) return "None";
  return pwd;
}

const char* GetHome()
{
  const char* home = getenv("HOME");
  if (home == NULL) return "/";
  return home;
}

void MakeCommandAndPrint()
{
  char line[SIZE];
  const char* username = GetUserName();
  const char* hostname = GetHostName();
  const char* cwd = GetCwd();
  
  SkipPath(cwd);
  snprintf(line, sizeof(line), "%s@%s %s", username, hostname, strlen(cwd) == 1 ? "/" : cwd+1);
  printf("[%s]> ", line);
  fflush(stdout);
}

int GetCommand(char command[], int size)
{
  char* s =  fgets(command, size, stdin);
  if (s == NULL) return -1;
  // 消除输入的回车
  command[strlen(command)-1] = '\0';
  return strlen(command);
}

void CommandSplit(char command[], int size)
{
  gArgv[0] = strtok(command, SEP);
  int i = 1;
  while(1)
  {
    gArgv[i] = strtok(NULL, SEP);
    if (gArgv[i] == NULL)
      break;
    i++;
  }
}


void ExecuteCommand()
{
  pid_t id = fork();
  if (id < 0) exit(1);
  else if (id == 0)
  {
    // child 
    execvp(gArgv[0], gArgv);
    exit(errno);
  }
  else
  {
    // father
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0)
    {
      // 等待成功
      lastcode = WEXITSTATUS(status);
      if (lastcode != 0)
        printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);
      
    }
  }
}

void Cd()
{
 const char* path = gArgv[1];
  if (path == NULL)
    path = GetHome(); // 选项为空就跳转家目录
  // 到这里 path 一定存在，改变路径
  chdir(path);

  // 刷新环境变量
  char tmp[SIZE*2];
  getcwd(tmp, sizeof(tmp)); // 获取绝对路径
  snprintf(cwd, sizeof(cwd), "PWD=%s", tmp);
  putenv(cwd);

}

int CheckBulidin()
{
  const char* enter_command = gArgv[0];

  // 检测
  int yes = 0;
  if (strcmp(enter_command, "cd") == 0)
  {
    yes = 1;
    Cd();
  }
  else if(strcmp(gArgv[0], "echo") == 0 && strcmp(gArgv[1], "$?") == 0)
  {
    yes = 1;
    printf("%d\n", lastcode);
    lastcode = 0;
  }
  return yes;
}

int main()
{
  while(1)
  {

     // 1.创建自己的命令行
     MakeCommandAndPrint();

     // 2.获取命令
     char command[SIZE];
     int n = GetCommand(command, sizeof(command));
     if (n <= 0) return 1;

     // 3.分割命令，存入命令行参数表
     CommandSplit(command, sizeof(command));

     // 4.内建命令
     n = CheckBulidin();
     if (n) continue;

     // 5.创建子进程，执行命令
     ExecuteCommand();
     sleep(2);
  }
  return 0;
}
